/*
 * @Author: hx 2542123711@QQ.com
 * @Date: 2025-01-10 21:07:26
 * @LastEditors: hx 2542123711@QQ.com
 * @LastEditTime: 2025-01-13 21:27:14
 * @FilePath: /class_socket/tcp_socket/tcpserver.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once

#include<iostream>
#include<cstring>
#include<unistd.h>
#include<string>
#include<pthread.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include"Log.hpp"
#include"ThreadPoll.hpp"
#include"Task.hpp"
#include"Daemon.hpp"

#define BUFFER_SIZE 4096

using namespace std;

const string defaultserverip = "0.0.0.0";
const uint16_t defaultserverport = 8080;
const int backlog = 5;
Log log;

enum
{
    SOCK_CREAT_ERROR = 1,
    SOCK_BIND_ERROR,
    SOCK_LISTEN_ERR
};

struct ThreadDate
{
    int _sockfd;
    string _clientip;
    uint16_t _clientport;

    ThreadDate(int sockfd, const string& clientip, const uint16_t& clientport)
    :_sockfd(sockfd), _clientip(clientip), _clientport(clientport)
    {}
};

class TcpServer
{
public:
    TcpServer(const string& serverip = defaultserverip, const uint16_t& serverport = defaultserverport)
    :_listensockfd(-1), _serverip(defaultserverip), _serverport(defaultserverport)
    {}

    void Init()
    {
        _listensockfd = socket(AF_INET, SOCK_STREAM,0);//流式套接字
        if(_listensockfd < 0)
        {
            log(Fatal, "_listensockfd create fail, %d,%s", errno, strerror(errno));
            exit(SOCK_CREAT_ERROR);
        }

        log(Info, "_listensockfd create success, _listensockfd:%d", _listensockfd);

        int opt = 1;
        setsockopt(_listensockfd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));//防止偶发性的服务器无法重启

        struct sockaddr_in server;
        socklen_t len = sizeof(server);
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());

        if(bind(_listensockfd, (struct sockaddr*)&server, len) < 0)
        {
            log(Fatal, "_listensockfd bind fail, %d,%s", errno, strerror(errno));
            exit(SOCK_BIND_ERROR);
        }

        log(Info, "_listensockfd bind success...");

        //Tcp是处于被动状态, 一直处于一种等待连接到来的时代listen...
        if(listen(_listensockfd, backlog) < 0)
        {
            log(Fatal, "listen fail, errono:%d, strerror:%s", errno, strerror(errno));
            exit(SOCK_LISTEN_ERR);
        }

        log(Info, "listen success, _listensockfd:%d", _listensockfd);
    }

    // void Service(int sockfd, const string& clientip, const uint16_t& clientport)
    // {
    //     char buffer[BUFFER_SIZE];
    //     while(true)
    //     {
    //         ssize_t n = read(sockfd, buffer, sizeof(buffer));
    //         if(n > 0)
    //         {
    //             buffer[n] = 0;
    //             cout << "client say# " << buffer << endl;
    //             string echo_string = "tcpserver echo# ";
    //             echo_string += buffer;

    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if(n == 0)
    //         {
    //             log(Info, "quit server close sockfd:%d, client ip:%s, client port:%d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //         else
    //         {
    //             log(Warning, "read error, sockfd:%d, client ip:%s, client port:%d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         } 
    //     }
    // }

    static void* Routine(void* args)
    {
        pthread_detach(pthread_self());

        ThreadDate* td = static_cast<ThreadDate*>(args);

        char buffer[BUFFER_SIZE];
        while(true)
        {
            ssize_t n = read(td->_sockfd, buffer, sizeof(buffer));
            if(n > 0)
            {
                buffer[n] = 0;
                cout << "client say# " << buffer << endl;
                string echo_string = "tcpserver echo# ";
                echo_string += buffer;

                write(td->_sockfd, echo_string.c_str(), echo_string.size());
            }
            else if(n == 0)
            {
                log(Info, "quit server close sockfd:%d, client ip:%s, client port:%d", td->_sockfd, td->_clientip.c_str(), td->_clientport);
                break;
            }
            else
            {
                log(Warning, "read error, sockfd:%d, client ip:%s, client port:%d", td->_sockfd, td->_clientip.c_str(), td->_clientport);
                break;
            } 
        }
    }

    void Start()
    {
        Daemon();

        ThreadPool<Task>::GetInstance()->Start();

        log(Info, "TcpServer is running...");
        
        while(true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            // 1.获取一个新的链接
            int sockfd = accept(_listensockfd, (struct sockaddr*)&client, &len);
            if(sockfd < 0)
            {
                log(Warning, "accept fail, errno:%d, strerror:%s", errno, strerror(errno));
                continue;
            }

            // 2.通过新的链接通信
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, (struct sockaddr*)&(client.sin_addr), clientip, sizeof(clientip));//把字节转成字符串

            log(Info, "get a new link..., sockfd:%d, clientip:%s, clientport:%d", sockfd, clientip, clientport);

            // version 1: 如果有多个客户端连接服务器, 服务器只会为第一个连接它的客户端服务, 只有第一个连接它的客户端退出了, 后面连接他的客户端才有用
            //Service(sockfd, clientip, clientport);
            //close(sockfd);

            // verson 2: 多进程: 让我们的孙子进程去执行service操作, 子进程直接返回, 孙子进程托孤给操作系统, 父进程继续accept接受发送请求的客户端
            //pid_t id = fork();
            //if(id == 0)
            //{
                //child
            //    close(_listensockfd);

            //    if(fork() > 0)//子进程
            //    {
            //        exit(0);
            //    }
            //    Service(sockfd, clientip, clientport);//孙子进程
            //    close(sockfd);
            //   exit(0);
            //}
            
            //father
            //close(sockfd);
            //子进程刚启动就exit, 所以waitpid立马返回, 父进程不会因为进程等待而阻塞在这里
            //pid_t ret = waitpid(id, nullptr, 0);
            //(void)ret;

            
            // version 3：多线程
            // unique_ptr<ThreadDate> up(new ThreadDate(sockfd, clientip, clientport));
            // ThreadDate* td = new ThreadDate(sockfd, clientip, clientport);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            // version 4: 线程池
            ThreadPool<Task>::GetInstance()->Push(Task(sockfd, clientip, clientport));
        }
    }
private:
    int _listensockfd;
    string _serverip;
    uint16_t _serverport;
};